// CmdLineParser.cpp: implementation of the CCmdParser class.
// 
// Copyright (c) Pavel Antonov, 2002
//
// This code is provided "as is", with absolutely no warranty expressed
// or implied. Any use is at your own risk.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <pgl/algotools/CmdLineParser.h>

namespace parsers
{
	//////////////////////////////////////////////////////////////////////
	// Construction/Destruction
	//////////////////////////////////////////////////////////////////////
	
	CCmdParser::CCmdParser(LPCTSTR sCmdLine, bool bCaseSensitive)
		: m_bCaseSensitive(bCaseSensitive)
	{
		m_sDelimeters = _T(" \t\n\r;,-/");
		m_sQuotes = _T("\"\'");
		m_sValueSep = _T("=:"); 
		
		if(sCmdLine) 
		{
			Parse(sCmdLine);
		}
	}
	
	CCmdParser::~CCmdParser()
	{
		m_ValueMap.clear();
	}
	
	bool CCmdParser::Parse(LPCTSTR sCmdLine) 
	{
		if(!sCmdLine) return false;
		
		m_sCmdLine = sCmdLine;
		m_ValueMap.clear();
		const CString sEmpty;
		LPCTSTR sArg,sQuote,sEndQuote;
		const CString szAll=m_sDelimeters+m_sValueSep+m_sQuotes;
		
		LPCTSTR sCurrent = sCmdLine;
		while(true) 
		{
			// /Key:"arg"
			if(_tcslen(sCurrent) == 0) 
				break; // No data left

			// ignore delimiter and separater
			sArg=_tcsspnp(sCurrent, szAll);
			if(!sArg || _tcslen(sArg) == 0) 
				break;

			LPCTSTR sVal = _tcspbrk(sArg, m_sValueSep);

			//end of string
			if(sVal == NULL)
				break;

			if(_tcslen(sVal)==1)
				break; // no value and end of string...
				
			// Key with no value or cmdline ends with /Key:
			CString csKey(sArg, sVal - sArg);
			if(csKey.IsEmpty()) 
				sCurrent = _tcsinc(sVal);

			if(!m_bCaseSensitive) 
			{
				csKey.MakeLower();
			}
				
			sVal = _tcsinc(sVal);
			
			// eat white...
			while(_tcslen(sVal)>0 && sVal[0]==' ')
				sVal=_tcsinc(sVal);

			// "arg"
			sQuote = _tcspbrk(sVal, m_sQuotes);
			sEndQuote=NULL;

			if(sQuote == sVal) 
			{ 
				// Quoted String
				sQuote = _tcsinc(sVal);
				sEndQuote = _tcspbrk(sQuote, m_sQuotes);
			} 
			else 
			{
				sQuote = sVal;
				sEndQuote = _tcschr(sQuote, _T(' '));
			}
				
			if(sEndQuote == NULL) 
			{ // No end quotes or terminating space, take rest of string
				CString csVal(sQuote);
				if(!csKey.IsEmpty()) 
				{ // Prevent /:val case
					m_ValueMap.insert(ValueMap::value_type(csKey, csVal));
				}
				break;
			}
			else 
			{  // End quote or space present
				if(!csKey.IsEmpty()) 
				{	// Prevent /:"val" case
					CString csVal(sQuote, sEndQuote - sQuote);
					m_ValueMap.insert(ValueMap::value_type(csKey, csVal));
				}
				sCurrent = _tcsinc(sEndQuote);
				continue;
			}
		}
		
		return !m_ValueMap.empty();
	}
	
	CCmdParser::ValueMap::const_iterator CCmdParser::FindKey(LPCTSTR sKey) const {
		CString s(sKey);
		if(!m_bCaseSensitive) {
			s.MakeLower();
		}
		return m_ValueMap.find(s);
	}
	// TRUE if "Key" present in command line
	bool CCmdParser::HasKey(LPCTSTR sKey, CString& sVal) const 
	{
		ValueMap::const_iterator it = FindKey(sKey);
		if(it == m_ValueMap.end()) 
		{
			sVal.Empty();
			return false;
		}

		sVal=it->second;
		return true;
	}
	
	// Is "key" present in command line and have some value
	bool CCmdParser::HasVal(LPCTSTR sKey) const 
	{
		ValueMap::const_iterator it = FindKey(sKey);
		if(it == m_ValueMap.end()) 
			return false;
		if(it->second.IsEmpty()) 
			return false;
		return true;
	}
	// Returns value if value was found or NULL otherwise
	LPCTSTR CCmdParser::GetVal(LPCTSTR sKey) const {
		ValueMap::const_iterator it = FindKey(sKey);
		if(it == m_ValueMap.end()) return false;
		return LPCTSTR(it->second);
	}
	// Returns true if value was found
	bool CCmdParser::GetVal(LPCTSTR sKey, CString& sValue) const {
		ValueMap::const_iterator it = FindKey(sKey);
		if(it == m_ValueMap.end()) return false;
		sValue = it->second;
		return true;
	}
};